/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */

package io.iec.edp.caf.multicontext.factory;

import io.iec.edp.caf.multicontext.annotation.Collect;
import io.iec.edp.caf.multicontext.support.Module;
import io.iec.edp.caf.multicontext.support.ModuleManager;
import org.springframework.beans.BeansException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.*;
import org.springframework.beans.factory.config.*;
import org.springframework.beans.factory.support.AbstractBeanFactory;
import org.springframework.beans.factory.support.AutowireCandidateResolver;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;

/**
 * 平台底座BeanFactory
 *
 * @author guowenchang
 */
public class PlatformBeanFactory extends DefaultListableBeanFactory {

//    private final String ENTITY_SCAN_PACKAGES = EntityScanPackages.class.getName();

    private final ModuleManager moduleManager;


    public PlatformBeanFactory(ModuleManager moduleManager) {
        super();
        this.moduleManager = moduleManager;
    }

    public ModuleManager getModuleManager() {
        return this.moduleManager;
    }

//    //---------------------------------------------------------------------
//    // Implementation of BeanFactory interface
//    //---------------------------------------------------------------------
//    @Override
//    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition){
//        super.registerBeanDefinition(beanName,beanDefinition);
//
//        if(beanDefinition.getSource()!=null && beanDefinition.getSource() instanceof MethodMetadata){
//            var moduleName="platform";
//            if(((MethodMetadata)beanDefinition.getSource()).getReturnTypeName().equalsIgnoreCase("io.iec.edp.caf.rest.RESTEndpoint")){
//                StartupAnalysisLog.countRestBean(moduleName);
//            }else if((MethodMetadata)((MethodMetadata) beanDefinition.getSource()).getAnnotationAttributes("RpcService")!=null){
//                StartupAnalysisLog.countRpcBean(moduleName);
//            }
//        }
//    }


    @Override
    public Object getBean(String name) throws BeansException {
        return doGetBeanByName(name, null, null);
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return (T) doGetBeanByName(name, requiredType, null);
    }

    @Override
    public Object getBean(String name, Object... args) throws BeansException {
        return doGetBeanByName(name, null, args);
    }

    @Override
    public <T> T getBean(Class<T> requiredType) throws BeansException {
        return doGetBeanByType(requiredType, null);
    }

    @Override
    public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
        return doGetBeanByType(requiredType, args);
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {
        try {
            return super.getBean(name, requiredType, args);
        } catch (NoSuchBeanDefinitionException ignored) {
        }

        if (name != null) {
            return (T) doGetBeanByName(name, requiredType, args);
        } else {
            return doGetBeanByType(requiredType, args);
        }
    }

    protected Object doGetBeanByName(String name, Class requiredType, Object[] args) throws BeansException {
        Object result = null;

        if (this.containsBean(name)) {
            if (args != null) {
                return super.getBean(name, args);
            } else {
                return super.getBean(name, requiredType);
            }
        }

        for (Module module : moduleManager) {
            BeanFactory beanFactory = module.getBeanFactory();
            if (beanFactory.containsBean(name)) {
                //todo 找到就返回
                if (result != null) {
                    throw new NoUniqueBeanDefinitionException(requiredType);
                }
                if (requiredType == null && args == null) {
                    result = beanFactory.getBean(name);
                } else if (requiredType == null) {
                    result = beanFactory.getBean(name, args);
                } else if (args == null) {
                    result = beanFactory.getBean(name, requiredType);
                }

                if (result != null) {
                    return result;
                }
            }
        }

        if (result != null) {
            return result;
        }

        throw new NoSuchBeanDefinitionException(name);
    }

    protected <T> T doGetBeanByType(Class<T> requiredType, Object[] args) throws BeansException {
        T result = null;

        try {
            return super.getBean(requiredType, args);
        } catch (NoSuchBeanDefinitionException ignored) {
        }

        for (Module module : moduleManager) {
            BeanFactory beanFactory = module.getBeanFactory();
            if (args == null) {
                try {
                    result = beanFactory.getBean(requiredType);
                } catch (NoSuchBeanDefinitionException ignored) {
                }
            } else {
                try {
                    result = beanFactory.getBean(requiredType, args);
                } catch (NoSuchBeanDefinitionException ignored) {
                }
            }

            if (result != null) {
                return result;
            }
        }

        result = null;
        try {
            result = super.getBean(requiredType, args);
        } catch (NoSuchBeanDefinitionException ignored) {
        }

        if (result != null) {
            return result;
        } else {
            throw new NoSuchBeanDefinitionException(requiredType);
        }
    }

    @Override
    public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
        BeanDefinition moduleResult = null, currentResult = null;

        try {
            currentResult = super.getBeanDefinition(beanName);
            return currentResult;
        } catch (NoSuchBeanDefinitionException e) {
            for (Module module : moduleManager) {
                ConfigurableListableBeanFactory beanFactory = module.getBeanFactory();
                if (beanFactory.containsBean(beanName)) {
                    try {
                        moduleResult = beanFactory.getBeanDefinition(beanName);
                        return moduleResult;
                    } catch (NoSuchBeanDefinitionException e1) {
                    }
                }
            }

            throw e;
        }
    }

    @Override
    public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException {
        if (!isTypeCollected(type)) {
            return super.getBeansOfType(type);
        }

        Map<String, T> resultMap = new HashMap<>();

        for (Module module : moduleManager) {
            ListableBeanFactory beanFactory = module.getBeanFactory();
            resultMap.putAll(beanFactory.getBeansOfType(type));
        }

        resultMap.putAll(super.getBeansOfType(type));
        return resultMap;
    }

    @Override
    public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
            throws BeansException {
        if (!isTypeCollected(type)) {
            return super.getBeansOfType(type, includeNonSingletons, allowEagerInit);
        }

        Map<String, T> resultMap = new HashMap<>();

        for (Module module : moduleManager) {
            ListableBeanFactory beanFactory = module.getBeanFactory();
            resultMap.putAll(beanFactory.getBeansOfType(type, includeNonSingletons, allowEagerInit));
        }

        resultMap.putAll(super.getBeansOfType(type, includeNonSingletons, allowEagerInit));
        return resultMap;
    }

//    @Override
//    public String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType) {
//        Set<String> resultList = new HashSet<>();
//
//        for (Module module : moduleManager) {
//            ListableBeanFactory beanFactory = module.getBeanFactory();
//            resultList.addAll(Arrays.asList(beanFactory.getBeanNamesForAnnotation(annotationType)));
//        }
//
//        resultList.addAll(Arrays.asList(super.getBeanNamesForAnnotation(annotationType)));
//        return resultList.toArray(new String[]{});
//    }

//    @Override
//    public Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType)
//            throws BeansException {
//        Map<String, Object> resultMap = new HashMap<>();
//
//        for (Module module : moduleManager) {
//            ListableBeanFactory beanFactory = module.getBeanFactory();
//            resultMap.putAll(beanFactory.getBeansWithAnnotation(annotationType));
//        }
//
//        resultMap.putAll(super.getBeansWithAnnotation(annotationType));
//        return resultMap;
//    }

    @Override
    public String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
        if (!isTypeCollected(type)) {
            return super.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
        }

        Set<String> resultList = new HashSet<>();

        for (Module module : moduleManager) {
            ListableBeanFactory beanFactory = module.getBeanFactory();
            resultList.addAll(Arrays.asList(beanFactory.getBeanNamesForType(type, includeNonSingletons, allowEagerInit)));
        }

        resultList.addAll(Arrays.asList(super.getBeanNamesForType(type, includeNonSingletons, allowEagerInit)));
        return resultList.toArray(new String[]{});
    }

    @Override
    public String[] getBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
        if (!isTypeCollected(type.getRawClass())) {
            return super.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
        }

        Set<String> resultList = new HashSet<>();

        for (Module module : moduleManager) {
            ListableBeanFactory beanFactory = module.getBeanFactory();
            resultList.addAll(Arrays.asList(beanFactory.getBeanNamesForType(type, includeNonSingletons, allowEagerInit)));
        }

        resultList.addAll(Arrays.asList(super.getBeanNamesForType(type, includeNonSingletons, allowEagerInit)));
        return resultList.toArray(new String[]{});
    }

    @Override
    @Nullable
    public Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException {
        String beanName = transformedBeanName(name);

        // Check manually registered singletons.
        Object beanInstance = getSingleton(beanName, false);
        if (beanInstance != null && !beanInstance.getClass().getName().contains("NullBean")) {
            if (beanInstance instanceof FactoryBean && !BeanFactoryUtils.isFactoryDereference(name)) {
                return getTypeForFactoryBean((FactoryBean<?>) beanInstance);
            } else {
                return beanInstance.getClass();
            }
        }

        // No singleton instance found -> check bean definition.
        if (!containsBeanDefinition(beanName)) {
            for (Module module : moduleManager) {
                ListableBeanFactory beanFactory = module.getBeanFactory();
                if (beanFactory.containsBeanDefinition(beanName)) {
                    return beanFactory.getType(originalBeanName(name));
                }
            }

            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null) {
                // No bean definition found in this factory -> delegate to parent.
                return parentBeanFactory.getType(originalBeanName(name));
            }
        }

        RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

        // Check decorated bean definition, if any: We assume it'll be easier
        // to determine the decorated bean's type than the proxy's type.
        BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
        if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) {
            RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
            Class<?> targetClass = predictBeanType(dbd.getBeanName(), tbd);
            if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) {
                return targetClass;
            }
        }

        Class<?> beanClass = predictBeanType(beanName, mbd);

        // Check bean class whether we're dealing with a FactoryBean.
        if (beanClass != null && FactoryBean.class.isAssignableFrom(beanClass)) {
            if (!BeanFactoryUtils.isFactoryDereference(name)) {
                // If it's a FactoryBean, we want to look at what it creates, not at the factory class.
                return getTypeForFactoryBean(beanName, mbd, allowFactoryBeanInit).resolve();
            } else {
                return beanClass;
            }
        } else {
            return (!BeanFactoryUtils.isFactoryDereference(name) ? beanClass : null);
        }
    }

    /**
     * Internal extended variant of {@link #isTypeMatch(String, ResolvableType)}
     * to check whether the bean with the given name matches the specified type. Allow
     * additional constraints to be applied to ensure that beans are not created early.
     *
     * @param name        the name of the bean to query
     * @param typeToMatch the type to match against (as a
     *                    {@code ResolvableType})
     * @return {@code true} if the bean type matches, {@code false} if it
     * doesn't match or cannot be determined yet
     * @throws NoSuchBeanDefinitionException if there is no bean with the given name
     * @see #getBean
     * @see #getType
     * @since 5.2
     */
    protected boolean isTypeMatch(String name, ResolvableType typeToMatch, boolean allowFactoryBeanInit)
            throws NoSuchBeanDefinitionException {

        String beanName = transformedBeanName(name);
        boolean isFactoryDereference = BeanFactoryUtils.isFactoryDereference(name);

        // Check manually registered singletons.
        Object beanInstance = getSingleton(beanName, false);
        if (beanInstance != null && !beanInstance.getClass().getName().contains("NullBean")) {
            if (beanInstance instanceof FactoryBean) {
                if (!isFactoryDereference) {
                    Class<?> type = getTypeForFactoryBean((FactoryBean<?>) beanInstance);
                    return (type != null && typeToMatch.isAssignableFrom(type));
                } else {
                    return typeToMatch.isInstance(beanInstance);
                }
            } else if (!isFactoryDereference) {
                if (typeToMatch.isInstance(beanInstance)) {
                    // Direct match for exposed instance?
                    return true;
                } else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) {
                    // Generics potentially only match on the target class, not on the proxy...
                    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                    Class<?> targetType = mbd.getTargetType();
                    if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance)) {
                        // Check raw class match as well, making sure it's exposed on the proxy.
                        Class<?> classToMatch = typeToMatch.resolve();
                        if (classToMatch != null && !classToMatch.isInstance(beanInstance)) {
                            return false;
                        }
                        if (typeToMatch.isAssignableFrom(targetType)) {
                            return true;
                        }
                    }
                    ResolvableType resolvableType = getTargetType(mbd);
                    if (resolvableType == null) {
                        resolvableType = getFactoryMethodReturnType(mbd);
                    }
                    return (resolvableType != null && typeToMatch.isAssignableFrom(resolvableType));
                }
            }
            return false;
        } else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
            // null instance registered
            return false;
        }

        // No singleton instance found -> check bean definition.
        if (!containsBeanDefinition(beanName)) {
            for (Module module : moduleManager) {
                ListableBeanFactory beanFactory = module.getBeanFactory();
                if (beanFactory.containsBeanDefinition(beanName)) {
                    return beanFactory.isTypeMatch(originalBeanName(name), typeToMatch);
                }
            }

            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null) {
                // No bean definition found in this factory -> delegate to parent.
                return parentBeanFactory.isTypeMatch(originalBeanName(name), typeToMatch);
            }
        }

        // Retrieve corresponding bean definition.
        RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
        BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();

        // Setup the types that we want to match against
        Class<?> classToMatch = typeToMatch.resolve();
        if (classToMatch == null) {
            classToMatch = FactoryBean.class;
        }
        Class<?>[] typesToMatch = (FactoryBean.class == classToMatch ?
                new Class<?>[]{classToMatch} : new Class<?>[]{FactoryBean.class, classToMatch});

        // Attempt to predict the bean type
        Class<?> predictedType = null;

        // We're looking for a regular reference but we're a factory bean that has
        // a decorated bean definition. The target bean should be the same type
        // as FactoryBean would ultimately return.
        if (!isFactoryDereference && dbd != null && isFactoryBean(beanName, mbd)) {
            // We should only attempt if the user explicitly set lazy-init to true
            // and we know the merged bean definition is for a factory bean.
            if (!mbd.isLazyInit() || allowFactoryBeanInit) {
                RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
                Class<?> targetType = predictBeanType(dbd.getBeanName(), tbd, typesToMatch);
                if (targetType != null && !FactoryBean.class.isAssignableFrom(targetType)) {
                    predictedType = targetType;
                }
            }
        }

        // If we couldn't use the target type, try regular prediction.
        if (predictedType == null) {
            predictedType = predictBeanType(beanName, mbd, typesToMatch);
            if (predictedType == null) {
                return false;
            }
        }

        // Attempt to get the actual ResolvableType for the bean.
        ResolvableType beanType = null;

        // If it's a FactoryBean, we want to look at what it creates, not the factory class.
        if (FactoryBean.class.isAssignableFrom(predictedType)) {
            if (beanInstance == null && !isFactoryDereference) {
                beanType = getTypeForFactoryBean(beanName, mbd, allowFactoryBeanInit);
                predictedType = beanType.resolve();
                if (predictedType == null) {
                    return false;
                }
            }
        } else if (isFactoryDereference) {
            // Special case: A SmartInstantiationAwareBeanPostProcessor returned a non-FactoryBean
            // type but we nevertheless are being asked to dereference a FactoryBean...
            // Let's check the original bean class and proceed with it if it is a FactoryBean.
            predictedType = predictBeanType(beanName, mbd, FactoryBean.class);
            if (predictedType == null || !FactoryBean.class.isAssignableFrom(predictedType)) {
                return false;
            }
        }

        // We don't have an exact type but if bean definition target type or the factory
        // method return type matches the predicted type then we can use that.
        if (beanType == null) {
            ResolvableType definedType = getTargetType(mbd);
            if (definedType == null) {
                definedType = getFactoryMethodReturnType(mbd);
            }
            if (definedType != null && definedType.resolve() == predictedType) {
                beanType = definedType;
            }
        }

        // If we have a bean type use it so that generics are considered
        if (beanType != null) {
            return typeToMatch.isAssignableFrom(beanType);
        }

        // If we don't have a bean type, fallback to the predicted type
        return typeToMatch.isAssignableFrom(predictedType);
    }

    @Override
    public boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
        String beanName = transformedBeanName(name);

        Object beanInstance = getSingleton(beanName, false);
        if (beanInstance != null) {
            if (beanInstance instanceof FactoryBean) {
                return (BeanFactoryUtils.isFactoryDereference(name) || ((FactoryBean<?>) beanInstance).isSingleton());
            } else {
                return !BeanFactoryUtils.isFactoryDereference(name);
            }
        }

        // No singleton instance found -> check bean definition.
        if (!containsBeanDefinition(beanName)) {
            for (Module module : moduleManager) {
                ListableBeanFactory beanFactory = module.getBeanFactory();
                if (beanFactory.containsBeanDefinition(beanName)) {
                    return beanFactory.isSingleton(originalBeanName(name));
                }
            }

            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null) {
                // No bean definition found in this factory -> delegate to parent.
                return parentBeanFactory.isSingleton(originalBeanName(name));
            }
        }

        RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

        // In case of FactoryBean, return singleton status of created object if not a dereference.
        if (mbd.isSingleton()) {
            if (isFactoryBean(beanName, mbd)) {
                if (BeanFactoryUtils.isFactoryDereference(name)) {
                    return true;
                }
                FactoryBean<?> factoryBean = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
                return factoryBean.isSingleton();
            } else {
                return !BeanFactoryUtils.isFactoryDereference(name);
            }
        } else {
            return false;
        }
    }

    /**
     * Return an instance, which may be shared or independent, of the specified bean.
     *
     * @param name          the name of the bean to retrieve
     * @param requiredType  the required type of the bean to retrieve
     * @param args          arguments to use when creating a bean instance using explicit arguments
     *                      (only applied when creating a new instance as opposed to retrieving an existing one)
     * @param typeCheckOnly whether the instance is obtained for a type check,
     *                      not for actual use
     * @return an instance of the bean
     * @throws BeansException if the bean could not be created
     */
    @SuppressWarnings("unchecked")
    public <T> T doGetBean(
            String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
            throws BeansException {

        String beanName = transformedBeanName(name);
        Object bean;

        // Eagerly check singleton cache for manually registered singletons.
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            if (logger.isTraceEnabled()) {
                if (isSingletonCurrentlyInCreation(beanName)) {
                    logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
                            "' that is not fully initialized yet - a consequence of a circular reference");
                } else {
                    logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
                }
            }
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        } else {
            // Fail if we're already creating this bean instance:
            // We're assumably within a circular reference.
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }

            // Check if bean definition exists in this factory.
            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (!containsBeanDefinition(beanName)) {
                String nameToLookup = originalBeanName(name);
                // Not found -> check module
                for (Module module : moduleManager) {
                    ListableBeanFactory beanFactory = module.getBeanFactory();
                    if (beanFactory.containsBeanDefinition(beanName)) {
                        return ((ModuleBeanFactory) beanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
                    }
                }

                // Not found -> check parent.
                if (parentBeanFactory != null && parentBeanFactory instanceof AbstractBeanFactory) {
                    return invokeDoGetBean(parentBeanFactory, nameToLookup, requiredType, args, typeCheckOnly);
                } else if (args != null) {
                    // Delegation to parent with explicit args.
                    return (T) parentBeanFactory.getBean(nameToLookup, args);
                } else if (requiredType != null) {
                    // No args -> delegate to standard getBean method.
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                } else {
                    return (T) parentBeanFactory.getBean(nameToLookup);
                }
            }

            if (!typeCheckOnly) {
                markBeanAsCreated(beanName);
            }

            try {
                RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                checkMergedBeanDefinition(mbd, beanName, args);

                // Guarantee initialization of beans that the current bean depends on.
                String[] dependsOn = mbd.getDependsOn();
                if (dependsOn != null) {
                    for (String dep : dependsOn) {
                        if (isDependent(beanName, dep)) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                        }
                        registerDependentBean(dep, beanName);
                        try {
                            getBean(dep);
                        } catch (NoSuchBeanDefinitionException ex) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                        }
                    }
                }

                // Create bean instance.
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            return createBean(beanName, mbd, args);
                        } catch (BeansException ex) {
                            // Explicitly remove instance from singleton cache: It might have been put there
                            // eagerly by the creation process, to allow for circular reference resolution.
                            // Also remove any beans that received a temporary reference to the bean.
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                } else if (mbd.isPrototype()) {
                    // It's a prototype -> create a new instance.
                    Object prototypeInstance = null;
                    try {
                        beforePrototypeCreation(beanName);
                        prototypeInstance = createBean(beanName, mbd, args);
                    } finally {
                        afterPrototypeCreation(beanName);
                    }
                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                } else {
                    String scopeName = mbd.getScope();
                    if (!StringUtils.hasLength(scopeName)) {
                        throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
                    }
                    Scope scope = getRegisteredScope(scopeName);
                    if (scope == null) {
                        throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                    }
                    try {
                        Object scopedInstance = scope.get(beanName, () -> {
                            beforePrototypeCreation(beanName);
                            try {
                                return createBean(beanName, mbd, args);
                            } finally {
                                afterPrototypeCreation(beanName);
                            }
                        });
                        bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                    } catch (IllegalStateException ex) {
                        throw new BeanCreationException(beanName,
                                "Scope '" + scopeName + "' is not active for the current thread; consider " +
                                        "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                                ex);
                    }
                }
            } catch (BeansException ex) {
                cleanupAfterBeanCreationFailure(beanName);
                throw ex;
            }
        }

        // Check if required type matches the type of the actual bean instance.
        if (requiredType != null && !requiredType.isInstance(bean)) {
            try {
                T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
                if (convertedBean == null) {
                    throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
                }
                return convertedBean;
            } catch (TypeMismatchException ex) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Failed to convert bean '" + name + "' to required type '" +
                            ClassUtils.getQualifiedName(requiredType) + "'", ex);
                }
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
        }
        return (T) bean;
    }

    @Override
    public boolean isPrototype(String name) throws NoSuchBeanDefinitionException {
        String beanName = transformedBeanName(name);

        // No singleton instance found -> check bean definition.
        if (!containsBeanDefinition(beanName)) {
            for (Module module : moduleManager) {
                ListableBeanFactory beanFactory = module.getBeanFactory();
                if (beanFactory.containsBeanDefinition(beanName)) {
                    return beanFactory.isPrototype(originalBeanName(name));
                }
            }

            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null) {
                // No bean definition found in this factory -> delegate to parent.
                return parentBeanFactory.isPrototype(originalBeanName(name));
            }
        }

        RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
        if (mbd.isPrototype()) {
            // In case of FactoryBean, return singleton status of created object if not a dereference.
            return (!BeanFactoryUtils.isFactoryDereference(name) || isFactoryBean(beanName, mbd));
        }

        // Singleton or scoped - not a prototype.
        // However, FactoryBean may still produce a prototype object...
        if (BeanFactoryUtils.isFactoryDereference(name)) {
            return false;
        }
        if (isFactoryBean(beanName, mbd)) {
            FactoryBean<?> fb = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
            if (System.getSecurityManager() != null) {
                return AccessController.doPrivileged(
                        (PrivilegedAction<Boolean>) () ->
                                ((fb instanceof SmartFactoryBean && ((SmartFactoryBean<?>) fb).isPrototype()) ||
                                        !fb.isSingleton()),
                        getAccessControlContext());
            } else {
                return ((fb instanceof SmartFactoryBean && ((SmartFactoryBean<?>) fb).isPrototype()) ||
                        !fb.isSingleton());
            }
        } else {
            return false;
        }
    }

    private ResolvableType getTargetType(RootBeanDefinition mbd) {
        Field field = null;
        try {
            field = RootBeanDefinition.class.getDeclaredField("targetType");
            field.setAccessible(true);
            ResolvableType resolvableType = (ResolvableType) field.get(mbd);
            return resolvableType;
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }

        return null;
    }

    private ResolvableType getFactoryMethodReturnType(RootBeanDefinition mbd) {
        Field field = null;
        try {
            field = RootBeanDefinition.class.getDeclaredField("factoryMethodReturnType");
            field.setAccessible(true);
            ResolvableType resolvableType = (ResolvableType) field.get(mbd);
            return resolvableType;
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }

        return null;
    }

    protected boolean isPrimary(String beanName, Object beanInstance) {
        String transformedBeanName = transformedBeanName(beanName);
        if (containsBeanDefinition(transformedBeanName)) {
            return getMergedLocalBeanDefinition(transformedBeanName).isPrimary();
        }

        for (Module module : moduleManager) {
            ConfigurableListableBeanFactory beanFactory = module.getBeanFactory();
            if (beanFactory.containsBeanDefinition(beanName)) {
                return beanFactory.getMergedBeanDefinition(transformedBeanName).isPrimary();
            }
        }

        BeanFactory parent = getParentBeanFactory();
        return isPrimary(parent, transformedBeanName, beanInstance);
    }

    /**
     * Determine whether the specified bean definition qualifies as an autowire candidate,
     * to be injected into other beans which declare a dependency of matching type.
     *
     * @param beanName   the name of the bean definition to check
     * @param descriptor the descriptor of the dependency to resolve
     * @param resolver   the AutowireCandidateResolver to use for the actual resolution algorithm
     * @return whether the bean should be considered as autowire candidate
     */
    protected boolean isAutowireCandidate(
            String beanName, DependencyDescriptor descriptor, AutowireCandidateResolver resolver)
            throws NoSuchBeanDefinitionException {
        String bdName = BeanFactoryUtils.transformedBeanName(beanName);

        for (Module module : moduleManager) {
            ConfigurableListableBeanFactory beanFactory = module.getBeanFactory();
            if (beanFactory.containsBeanDefinition(bdName) || beanFactory.containsSingleton(bdName)) {
                return beanFactory.isAutowireCandidate(beanName, descriptor);
            }
        }

        return super.isAutowireCandidate(beanName, descriptor, resolver);
    }

    private boolean isPrimary(BeanFactory parent, String transformedBeanName, Object beanInstance) {
        if (parent instanceof DefaultListableBeanFactory) {
            try {
                Method method = DefaultListableBeanFactory.class.getDeclaredMethod("isPrimary", String.class, Object.class);
                method.setAccessible(true);
                return (boolean) method.invoke(parent, transformedBeanName, beanInstance);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }

        }

        return false;
    }

    private <T> T invokeDoGetBean(BeanFactory parentBeanFactory, String nameToLookup, Class<T> requiredType,
                                  Object[] args, boolean typeCheckOnly) throws BeansException {
        if (parentBeanFactory instanceof DefaultListableBeanFactory) {
            try {
                Method method = AbstractBeanFactory.class.getDeclaredMethod("doGetBean", String.class, Class.class, Object[].class, boolean.class);
                method.setAccessible(true);
                return (T) method.invoke(parentBeanFactory, nameToLookup, requiredType, args, typeCheckOnly);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                Throwable ex = e.getTargetException();
                if (ex instanceof BeansException) {
                    throw (BeansException) ex;
                } else {
                    throw (RuntimeException) ex;
                }
            }
        }

        return null;
    }

    @Override
    public boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException {
        String beanName = transformedBeanName(name);
        Object beanInstance = getSingleton(beanName, false);
        if (beanInstance != null) {
            return (beanInstance instanceof FactoryBean);
        }
        // No singleton instance found -> check bean definition.
        if (!containsBeanDefinition(beanName)) {
            for (Module module : moduleManager) {
                ListableBeanFactory beanFactory = module.getBeanFactory();
                if (beanFactory.containsBeanDefinition(beanName)) {
                    return module.getBeanFactory().isFactoryBean(beanName);
                }
            }

            if (getParentBeanFactory() instanceof ConfigurableBeanFactory) {
                // No bean definition found in this factory -> delegate to parent.
                return ((ConfigurableBeanFactory) getParentBeanFactory()).isFactoryBean(name);
            }
        }

        return isFactoryBean(beanName, getMergedLocalBeanDefinition(beanName));
    }

    @Override
    public void registerResolvableDependency(Class<?> dependencyType, @Nullable Object autowiredValue) {
        super.registerResolvableDependency(dependencyType, autowiredValue);
        for (Module module : moduleManager) {
            module.getBeanFactory().registerResolvableDependency(dependencyType, autowiredValue);
        }
    }

    /**
     * 判断某个bean是否需要从各个模块收集，比如restendpoint这种，所以就需要打collect的注解
     * @param type
     * @return
     */
    private boolean isTypeCollected(Class<?> type) {
        if (type.getName().equals("javax.servlet.Filter")) {
            return true;
        }

        Annotation[] annotations = type.getAnnotations();
        for (Annotation annotation : annotations) {
            if (annotation.annotationType() == Collect.class) {
                return true;
            }
        }

        return false;
    }
}
